Async in depth

A tutorial to explain how async and Promises work in depth. It follows on an excellent article about async/await and expands it to be used with server.

This is the canonical and recommended way of using async with server:

const home = get('/', async () => {
  // do some async work here
  return 'Hello world';
});

All snippets are simplified and we are only showing the router part. See at the end of the tutorial for a full example.

The async function is equivalent to a function that returns a promise when called and the return in the end is the value that it will resolve to. So the code above is the same as this:

const home = get('/', () => new Promise((resolve, reject) => {
  // do some async work here
  resolve('Hello world');
}));

We can instantly see how the canonical example is a lot more clear and easy to understand while it doesn't have any disadvantage.

Of course you might not need any async thing, so you can just use a normal function with the return value. It is conveniently similar to the canonical example:

const home = get('/', () => {
  // do some sync work here
  return 'Hello world';
});

await

Let's do some actual work! Let's say that we want to scrape that website of yours. We will be checking on https://www.libre.university/ to avoid making anyone angry. We are going to see how to do it in several ways through a simple page status checker.

First let's use a small async wrapping library to get the status of the page:

const request = require('request-promises');

const home = get('/', async () => {
  const res = await request('https://www.libre.university/');
  return `Status: ${res.statusCode}`;
});

This is the ideal case when there is a library such as request-promises that makes it easy. They are really easy to make and we recommend that you make them if needed. Check also mz, which wraps Node.js' default methods.

But things are not always so bright. Let's now see what can be done when the library that you want to use has a callback-based workflow:

const request = require('request');

const home = get('/', async () => {
  const res = await new Promise((resolve, reject) => {
    request('https://www.libre.university/', (err, res) => {
      err ? reject(err) : resolve(res);
    });
  });
  return `Status: ${res.statusCode}`;
});

As you can see things get ugly pretty soon. It is recommended that you take a step further and put it in a separate dependency, which is easy with modern javascript:

// request.js
const request = require('request');
module.exports = options => new Promise((resolve, reject) => {
  request(options, (err, res) => {
    err ? reject(err) : resolve(res);
  });
});

Then using it in your project is the same as the example shown above about request-promises.

The full status checker example ready to be run:

const server = require('server');
const { get } = server.router;
const request = require('request-promises');

const home = get('/', async () => {
  const res = await request('https://www.libre.university/');
  return `Status: ${res.statusCode}`;
});

server(home);